Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/project-time-travel #2329

Merged
merged 34 commits into from
Jan 30, 2025
Merged

Feature/project-time-travel #2329

merged 34 commits into from
Jan 30, 2025

Conversation

mohitb35
Copy link
Collaborator

@mohitb35 mohitb35 commented Dec 11, 2024

Pending

  • Set up API for ESRI wayback machine data)
  • Source / year selection should update the data (basic functionality is working)
  • tab functionality should show/hide the view (basic functionality is working)
  • site selection change should update the data (basic functionality is working)
  • Prevent data from having to be reloaded on subsequent loads
  • zoom buttons - should these be combined or hidden? (Currently combined, seems to work well. Let's revisit this if needed)
  • Smoother initial loading when options change (site, year, source)
  • close popup controls on clicks outside. This includes site selection, year selection, source selection. - separate PR (issue Add click outside handler to close time travel dropdown #2379 created)
  • Known issue - gets stuck on Loading screen at times
  • Known issue - mobile UI - separate PR (issue mobile ui for time travel/map controls #2390 created)

Cleanup

Changes to load time travel data by hardcoded values using getTimeTravelConfig()

Copy link

vercel bot commented Dec 11, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
planet-webapp-multi-tenancy-setup ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 30, 2025 8:20am
2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
planet-webapp ⬜️ Ignored (Inspect) Jan 30, 2025 8:20am
planet-webapp-temp ⬜️ Ignored (Inspect) Jan 30, 2025 8:20am

Copy link
Contributor

coderabbitai bot commented Dec 11, 2024

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm warn config production Use --omit=dev instead.
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: [email protected]
npm error Found: [email protected]
npm error node_modules/eslint
npm error dev eslint@"^8.26.0" from the root project
npm error peer eslint@"6 || 7 || 8" from @emotion/[email protected]
npm error node_modules/@emotion/eslint-plugin
npm error dev @emotion/eslint-plugin@"^11.12.0" from the root project
npm error 15 more (@eslint-community/eslint-utils, ...)
npm error
npm error Could not resolve dependency:
npm error peer eslint@"^5.16.0 || ^6.8.0 || ^7.2.0" from [email protected]
npm error node_modules/eslint-config-airbnb
npm error dev eslint-config-airbnb@"^18.2.0" from the root project
npm error
npm error Conflicting peer dependency: [email protected]
npm error node_modules/eslint
npm error peer eslint@"^5.16.0 || ^6.8.0 || ^7.2.0" from [email protected]
npm error node_modules/eslint-config-airbnb
npm error dev eslint-config-airbnb@"^18.2.0" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /.npm/_logs/2025-01-30T08_13_09_904Z-eresolve-report.txt
npm error A complete log of this run can be found in: /.npm/_logs/2025-01-30T08_13_09_904Z-debug-0.log

Walkthrough

This pull request introduces a comprehensive set of changes across multiple files in the project, focusing on enhancing the project map and time travel functionality. The modifications include adding a new React functional component FieldDataIcon, updating import paths for several icon components, and enhancing the ProjectPropsContext with new state management features. Additionally, new styling for map-related interfaces is introduced, along with the implementation of the TimeTravel component for geographical data visualization. The changes primarily revolve around improving the user interface for project maps and integrating time travel features.

Changes

File Change Summary
public/assets/images/icons/FieldDataIcon.tsx New React functional component for rendering a field data icon
public/assets/images/icons/projectV2/CalendarIcon.tsx Updated import path for IconProps type
public/assets/images/icons/projectV2/DropdownDownArrow.tsx Updated import path for IconProps type
public/assets/images/icons/projectV2/DropdownUpArrow.tsx Updated import path for IconProps type
src/features/common/Layout/ProjectPropsContext.tsx Updated type imports and state initialization
src/features/projects/components/maps/Project.tsx Commented out raster data state updates
src/features/projectsV2/ProjectDetails/index.tsx Enhanced project loading logic with time travel configuration
src/features/projectsV2/ProjectsMap/InterventionDropDown/index.tsx Updated import paths for dropdown icons and corrected interface syntax
src/features/projectsV2/ProjectsMap/MapControls.tsx Added selectedTab property to MapControlsProps interface
src/features/projectsV2/ProjectsMap/ProjectMapTabs/index.tsx Introduced new MapTabs component for tab navigation
src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx Introduced TimeTravel component for side-by-side geographical data comparison
src/utils/mapsV2/timeTravel.ts New utility module for handling time travel data configuration
Multiple SCSS files Added new styles for map, dropdown, and tab components
src/features/projectsV2/ProjectsMap/stories/SingleMapTab.stories.tsx Updated import paths for SatelliteIcon and SingleTab components
src/features/projectsV2/ProjectsMap/stories/Tabs.stories.tsx Modified import path for Tabs component and updated property name in args
src/features/projectsV2/ProjectsMap/stories/TimeTravelDropdown.stories.tsx Added argTypes for TimeTravelDropdown component in Storybook

Possibly related PRs

  • Implement mobile UI for MapFeatureExplorer #2213: The ExploreIcon component was modified to include a default value for the width property, which enhances usability similar to how the FieldDataIcon component accepts a color prop for dynamic rendering.
  • Hotfix/intervention-filter #2370: The changes in the InterventionDropDown component involve adding a new prop hasProjectSites, which influences the rendering of the dropdown, similar to how the FieldDataIcon component's rendering is influenced by its color prop.

Suggested Labels

PR: reviewed-approved

Suggested Reviewers

  • mariahosfeld
  • norbertschuler

Poem

🐰 Hopping through code with glee,
Maps that travel through history!
Tabs and icons, colors bright,
Time's layers dancing in my sight.
Code rabbit's map adventure, oh so neat! 🗺️

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

- enables switching between time travel and field data modes
- 'Satellite Analysis' is currently disabled
- Add error handling with ErrorHandlingContext
- Add loading states and visual feedback
- Improve data validation and map initialization
- Add configurable year/source selection dropdowns
- Add view state syncing with main map
- Optimize performance with proper cleanup and memoization
- Improve TypeScript types and error constants
- mount TimeTravel only when selectedTab = timeTravel for the first time
- unmount TimeTravel only while moving off the single project page
- prevent unnecessary reloads of tiles in comparison maps
@mohitb35 mohitb35 force-pushed the feature/project-time-travel branch from 805bb7f to f36ccf7 Compare January 2, 2025 09:35
…ering

- plant locations not shown if selected tab is not field data
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/features/projectsV2/TimeTravelDropdown/index.tsx (2)

51-52: Consider memoizing the helper function.

The isOptionSelected function could benefit from memoization to prevent unnecessary re-renders when used in the render logic.

-const isOptionSelected = (option: string, selectedValue: string): boolean =>
-  option.toLowerCase() === selectedValue.toLowerCase();
+const isOptionSelected = React.useCallback(
+  (option: string, selectedValue: string): boolean =>
+    option.toLowerCase() === selectedValue.toLowerCase(),
+  []
+);

63-69: Improve type safety for translations.

The translation key and rich text parameters lack type safety. Consider creating a type-safe translation setup.

-{tTimeTravel.rich('sourceAttributionLabel', {
+type TimeTravelTranslations = {
+  sourceAttributionLabel: {
+    year: string;
+    source: string;
+    highlight: (chunks: string) => JSX.Element;
+  };
+};
+
+{tTimeTravel.rich<keyof TimeTravelTranslations>('sourceAttributionLabel', {
   year: selectedYear,
   source: SOURCE_LABELS[selectedSource],
   highlight: (chunks) => (
     <span className={styles.highlighted}>{chunks}</span>
   ),
})}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5bd72f and 05af2c7.

📒 Files selected for processing (1)
  • src/features/projectsV2/TimeTravelDropdown/index.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
src/features/projectsV2/TimeTravelDropdown/index.tsx (1)

14-23: LGTM! Well-structured interface definition.

The TimeTravelDropdownProps interface is well-defined with proper TypeScript types and clear prop names.

- for improved modularity and readability
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
src/utils/mapsV2/timeTravel.ts (3)

24-42: Add error handling and consider configuration-driven approach.

The function could benefit from:

  1. Error handling for invalid or missing configuration.
  2. Moving hardcoded values to a configuration file.
+const CONFIG_ERROR = 'Invalid or missing time travel configuration';
+
 export const getTimeTravelConfig = (): TimeTravelConfig => {
   const result: TimeTravelConfig = {};
+  
+  if (!timeTravelConfig) {
+    throw new Error(CONFIG_ERROR);
+  }
 
   for (const source of SOURCE_NAMES) {
     const sourceData = timeTravelConfig[source]?.wayback;
-    if (!sourceData) continue;
+    if (!sourceData) {
+      console.warn(`No wayback data found for source: ${source}`);
+      continue;
+    }

47-52: Add input validation for URL format.

The function should validate that the input URL contains the expected WMTS format parameters before performing replacements.

 const convertToZYXFormat = (url: string): string => {
+  const requiredParams = ['{level}', '{row}', '{col}'];
+  if (!requiredParams.every(param => url.includes(param))) {
+    throw new Error('Invalid WMTS URL format');
+  }
+
   return url
     .replace('{level}', '{z}')
     .replace('{row}', '{y}')
     .replace('{col}', '{x}');
 };

59-81: Add input validation and consider performance optimization.

The function should validate input and could be optimized for large datasets.

 const getLatestByYear = (items: WaybackItem[]): SingleYearTimeTravelData[] => {
+  if (!Array.isArray(items)) {
+    throw new Error('Invalid input: expected array of WaybackItem');
+  }
+
+  // Pre-sort items by releaseDatetime for better performance with large datasets
+  items.sort((a, b) => b.releaseDatetime - a.releaseDatetime);
+
   const intermediate = items.reduce<
     Record<string, { rasterUrl: string; timestamp: number }>
   >((acc, item) => {
     const year = new Date(item.releaseDatetime).getFullYear().toString();
-    const existing = acc[year];
-
-    if (!existing || item.releaseDatetime > existing.timestamp) {
+    if (!acc[year]) {
       acc[year] = {
         rasterUrl: convertToZYXFormat(item.itemURL),
         timestamp: item.releaseDatetime,
       };
     }
src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx (4)

27-43: Move configuration constants to a dedicated file.

Consider extracting the map configuration constants and error codes to a dedicated configuration file for better maintainability.

Create a new file src/features/projectsV2/ProjectsMap/TimeTravel/config.ts:

export const MAP_CONFIG = {
  EMPTY_STYLE: {
    version: 8,
    sources: {},
    layers: [],
  },
  DEFAULT_SOURCE: 'esri',
  DEFAULT_YEARS: {
    BEFORE: '2014',
    AFTER: '2021',
  },
};

export const MAP_ERROR_CODES = {
  INITIALIZATION: 'MAP_INIT_ERROR',
  DATA_MISSING: 'MAP_DATA_ERROR',
  LAYER_LOAD: 'MAP_LAYER_ERROR',
  INVALID_SOURCE: 'MAP_SOURCE_ERROR',
  INVALID_YEAR: 'MAP_YEAR_ERROR',
} as const;

66-71: Combine related state variables.

Consider using a single state object for loading states and dropdown states to reduce the number of state variables.

-  const [isLoading, setIsLoading] = useState(true);
-  const [beforeLoaded, setBeforeLoaded] = useState(false);
-  const [afterLoaded, setAfterLoaded] = useState(false);
-  const [beforeDropdownOpen, setBeforeDropdownOpen] = useState(false);
-  const [afterDropdownOpen, setAfterDropdownOpen] = useState(false);
+  const [mapState, setMapState] = useState({
+    isLoading: true,
+    beforeLoaded: false,
+    afterLoaded: false,
+    dropdowns: {
+      before: false,
+      after: false
+    }
+  });

109-146: Extract validation logic to a separate utility function.

The validation logic in validateData is complex and could be moved to a separate utility function for better maintainability and reusability.

Create a new file src/features/projectsV2/ProjectsMap/TimeTravel/utils.ts:

export const validateTimeTravelData = (
  sitesGeoJson: FeatureCollection,
  timeTravelConfig: ProjectTimeTravelConfig,
  selectedSourceBefore: SourceName,
  selectedSourceAfter: SourceName,
  selectedYearBefore: string,
  selectedYearAfter: string
) => {
  if (!sitesGeoJson?.features) {
    throw new Error('Invalid or missing GeoJSON data');
  }
  // ... rest of validation logic
};

484-484: Enhance loading overlay with progress information.

Consider adding more detailed loading information and possibly a progress indicator.

-{isLoading && <div className={styles.loadingOverlay}>Loading...</div>}
+{isLoading && (
+  <div className={styles.loadingOverlay}>
+    <div className={styles.loadingSpinner} />
+    <div className={styles.loadingText}>
+      Loading satellite imagery...
+      {!beforeLoaded && <div>Initializing before map...</div>}
+      {!afterLoaded && <div>Initializing after map...</div>}
+    </div>
+  </div>
+)}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05af2c7 and 816df46.

📒 Files selected for processing (2)
  • src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx (1 hunks)
  • src/utils/mapsV2/timeTravel.ts (1 hunks)
🔇 Additional comments (3)
src/utils/mapsV2/timeTravel.ts (2)

97-98: Address TODO comment regarding cache time.

The cache time is temporarily set to 5 minutes. Consider:

  1. Implementing a proper cache invalidation strategy.
  2. Making the cache duration configurable based on data update frequency.

Would you like me to help implement a more robust caching strategy?


106-106: Address TODO comment regarding zoom level.

The zoom level is hardcoded to 13. Consider:

  1. Making it configurable based on project requirements.
  2. Documenting why this specific zoom level was chosen.

Would you like me to help determine and implement the optimal zoom level configuration?

src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx (1)

292-292: Update ESRI attribution text.

Replace the generic 'layer attribution' with ESRI's official attribution text.

src/utils/mapsV2/timeTravel.ts Outdated Show resolved Hide resolved
src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx (3)

126-128: Enhance error messages for better user experience.

Consider making error messages more user-friendly and actionable. For example:

-        `Year ${selectedYearBefore} not found in source ${selectedSourceBefore}`
+        `Selected year ${selectedYearBefore} is not available for ${selectedSourceBefore}. Please choose a different year.`
-        `Year ${selectedYearAfter} not found in source ${selectedSourceAfter}`
+        `Selected year ${selectedYearAfter} is not available for ${selectedSourceAfter}. Please choose a different year.`

Also applies to: 135-137


229-235: Enhance cleanup logic for comparison instance.

The cleanup function should also reset the loading state and loaded states to ensure proper re-initialization if the component remounts.

 return () => {
   beforeMap.remove();
   afterMap.remove();
   if (compareRef.current) {
     compareRef.current.remove();
+    compareRef.current = null;
   }
+  setIsLoading(true);
+  setBeforeLoaded(false);
+  setAfterLoaded(false);
 };

416-416: Remove unnecessary spread operator usage.

The spread operator usage here is unnecessary as timeTravelConfig is already an object with the required structure.

-        { ...timeTravelConfig, sources: timeTravelConfig.sources },
+        timeTravelConfig,

Also applies to: 426-426

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 816df46 and b3a25dd.

📒 Files selected for processing (1)
  • src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (5)
src/features/projectsV2/ProjectsMap/TimeTravel/index.tsx (5)

1-44: Well-structured imports and constants!

The code demonstrates good TypeScript practices with proper type imports and const assertions for error codes.


191-242: Add error boundary for map initialization.

The map initialization could benefit from being wrapped in an error boundary component to handle rendering failures gracefully.


317-317: Update attribution to ESRI's official text.

Replace the generic 'layer attribution' with ESRI's official attribution text.


484-503: Add click outside handling for dropdowns.

The dropdowns should close when clicking outside of them.


89-100: Consider more robust state initialization.

The current initialization of selected years and sources with default values could cause issues if the available data doesn't include these specific years (2014, 2021). Consider initializing with undefined and adding validation logic, or ensure these defaults always exist in the data.

✅ Verification successful

Default year values are properly aligned with data boundaries

The default years (2014 and 2021) are safe to use as they match the actual boundaries of the available time travel data, with 2014 being the earliest available year and 2021 being the latest, as confirmed in the time travel configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if default years exist in time travel configurations
rg -l "2014|2021" --type json

Length of output: 363


Script:

#!/bin/bash
# Check the content of relevant JSON files
echo "=== Content of time-travel.json ==="
cat public/data/maps/time-travel.json | jq '.'

echo -e "\n=== Content of config.json ==="
cat src/utils/mapsV2/config.json | jq '.'

# Also find where these default years are defined
echo -e "\n=== Default years definition ==="
rg "DEFAULT_(BEFORE|AFTER)_YEAR.*=.*\b(2014|2021)\b" -A 1 -B 1

Length of output: 70645

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/utils/mapsV2/timeTravel.ts (2)

19-21: Consider moving WMTS base URLs to configuration.

The WMTS base URL is currently hardcoded. Consider moving it to the time-travel.json configuration file for better maintainability and environment-specific configurations.


59-81: Add input validation and improve documentation.

The data processing logic is complex and could benefit from:

  1. Input validation for the items array
  2. Additional comments explaining the reduction logic
 const getLatestByYear = (items: WaybackItem[]): SingleYearTimeTravelData[] => {
+  if (!Array.isArray(items)) {
+    throw new Error('Invalid input: expected array of WaybackItem');
+  }
+
+  // Group items by year, keeping only the latest entry for each year
   const intermediate = items.reduce<
     Record<string, { rasterUrl: string; timestamp: number }>
   >((acc, item) => {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3a25dd and 8843cc3.

📒 Files selected for processing (1)
  • src/utils/mapsV2/timeTravel.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/utils/mapsV2/timeTravel.ts (2)
Learnt from: mohitb35
PR: Plant-for-the-Planet-org/planet-webapp#2329
File: src/utils/mapsV2/timeTravel.ts:100-117
Timestamp: 2025-01-27T08:15:26.468Z
Learning: When validating GeoJSON Point coordinates at runtime, check if coordinates is an array and has exactly 2 elements (X,Y coordinates). The TypeScript Point type handles null checks, while the Position type allows for an optional third Z coordinate according to the GeoJSON spec.
Learnt from: mohitb35
PR: Plant-for-the-Planet-org/planet-webapp#2329
File: src/utils/mapsV2/timeTravel.ts:100-117
Timestamp: 2025-01-27T08:10:51.101Z
Learning: In geospatial applications, while TypeScript types (like `Point` from `geojson`) provide compile-time safety, runtime validation of coordinates is still necessary when dealing with external data sources to ensure data integrity and prevent runtime errors.
🔇 Additional comments (6)
src/utils/mapsV2/timeTravel.ts (6)

44-52: LGTM! Well-documented utility function.

The URL format conversion function is clear, concise, and properly documented.


83-91: LGTM! Well-structured type definitions.

The type definitions are clear, properly exported, and follow TypeScript best practices.


97-98: Address TODO comment regarding cache time.

The cache time is temporarily set to 5 minutes instead of 24 hours. Please confirm and update the final cache duration.


113-113: Address TODO comment regarding zoom level.

The zoom level is hardcoded to 13. Please confirm and document the appropriate zoom level for the application's needs.


100-106: LGTM! Proper coordinate validation.

The coordinate validation follows best practices for GeoJSON Point validation at runtime, as discussed in previous reviews.


24-42: Add error handling for malformed configuration data.

The function should validate the imported configuration data and handle potential errors gracefully. Consider adding try-catch blocks and data validation.

 export const getTimeTravelConfig = (): TimeTravelConfig => {
+  if (!timeTravelConfig || typeof timeTravelConfig !== 'object') {
+    console.error('Invalid time travel configuration');
+    return {};
+  }
   const result: TimeTravelConfig = {};
   // ... rest of the function

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/features/projectsV2/TimeTravelDropdown/index.tsx (4)

1-1: Consider using path aliases for imports.

The imports for icons use multiple parent directory references (../../../../). Consider using path aliases (e.g., @assets/images/icons/...) to improve maintainability and readability.

Also applies to: 6-8


41-44: Add error handling and memoize callbacks.

The change handlers should include error handling and be memoized to prevent unnecessary re-renders.

+import React, { useState, useCallback } from 'react';
+
-const handleChangeYear = (year: string) => {
+const handleChangeYear = useCallback((year: string) => {
+  try {
     setSelectedYear(year);
     onYearChange(year);
+  } catch (error) {
+    console.error('Error updating year:', error);
+  }
-};
+}, [onYearChange]);

-const handleChangeSource = (source: SourceName) => {
+const handleChangeSource = useCallback((source: SourceName) => {
+  try {
     setSelectedSource(source);
     onSourceChange(source);
+  } catch (error) {
+    console.error('Error updating source:', error);
+  }
-};
+}, [onSourceChange]);

Also applies to: 46-49


83-98: Add type safety to event handlers.

The click handlers should include proper event types for better type safety.

-onClick={() => handleChangeYear(year)}
+onClick={(e: React.MouseEvent<HTMLTimeElement>) => handleChangeYear(year)}

101-113: Add proper ARIA attributes to source list items.

The source list items should have proper ARIA attributes for better accessibility.

 <li
   key={index}
+  role="option"
+  aria-selected={isOptionSelected(source, selectedSource)}
   onClick={() => handleChangeSource(source)}
   className={`${
     isOptionSelected(source, selectedSource)
       ? styles.selectedMenuItem
       : styles.unselectedMenuItem
   }`}
 >
   {SOURCE_LABELS[source]}
 </li>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8843cc3 and 84f1c83.

⛔ Files ignored due to path filters (1)
  • public/static/locales/en/projectDetails.json is excluded by !**/*.json
📒 Files selected for processing (1)
  • src/features/projectsV2/TimeTravelDropdown/index.tsx (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/features/projectsV2/TimeTravelDropdown/index.tsx (1)
Learnt from: mohitb35
PR: Plant-for-the-Planet-org/planet-webapp#2329
File: src/features/projectsV2/TimeTravelDropdown/index.tsx:37-39
Timestamp: 2025-01-23T09:31:12.093Z
Learning: The TimeTravelDropdown component in src/features/projectsV2/TimeTravelDropdown/index.tsx follows an uncontrolled component pattern where defaultYear, defaultSource and isOpen props are used for initial values only, with callbacks to notify parent of changes.
🔇 Additional comments (3)
src/features/projectsV2/TimeTravelDropdown/index.tsx (3)

14-23: LGTM! Well-structured props interface.

The TimeTravelDropdownProps interface is well-defined with proper types and clear naming.


121-121: LGTM! Proper export statement.

The default export follows React conventions.


10-12: Verify completeness of SOURCE_LABELS.

The SOURCE_LABELS object only includes 'esri'. Ensure this covers all possible values of SourceName type to prevent potential runtime errors.

✅ Verification successful

SOURCE_LABELS is complete and type-safe

The SOURCE_LABELS object correctly includes all possible values of SourceName type, which currently only allows 'esri' as a valid source. No changes needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for SourceName type definition and its usage
ast-grep --pattern 'type SourceName = $$$'
rg -A 5 'SourceName'

Length of output: 6024


Script:

#!/bin/bash
# Search for SOURCE_NAMES constant definition
ast-grep --pattern 'const SOURCE_NAMES = $$$'
rg -A 2 'SOURCE_NAMES ='

Length of output: 337

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/utils/mapsV2/timeTravel.ts (3)

20-22: Consider moving the ESRI URL to environment variables.

The ESRI map tiles URL should be configurable through environment variables to support different environments and make it easier to update.

+// In .env file
+NEXT_PUBLIC_ESRI_MAPTILES_URL=https://wayback.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/WMTS/1.0.0/default028mm/MapServer/tile

+// In constants file
+const ESRI_MAPTILES_URL = process.env.NEXT_PUBLIC_ESRI_MAPTILES_URL;

 const SOURCE_BASE_URLS: Record<SourceName, string> = {
-  esri: 'https://wayback.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/WMTS/1.0.0/default028mm/MapServer/tile',
+  esri: ESRI_MAPTILES_URL,
 };

4-5: Track removal of legacy time travel configuration.

This code segment and the associated JSON file are marked for removal. Consider creating a tracking issue to ensure this technical debt is addressed.

Would you like me to create an issue to track the removal of the legacy time travel configuration?

Also applies to: 24-44


61-83: Enhance type safety in data transformation.

While the implementation is efficient, we can improve type safety by:

  1. Using a more specific type for the timestamp
  2. Adding type guard for the intermediate record
+type TimestampMS = number;
+interface YearData {
+  rasterUrl: string;
+  timestamp: TimestampMS;
+}

-const getLatestByYear = (items: WaybackItem[]): SingleYearTimeTravelData[] => {
+const getLatestByYear = (items: WaybackItem[]): SingleYearTimeTravelData[] => {
   const intermediate = items.reduce<
-    Record<string, { rasterUrl: string; timestamp: number }>
+    Record<string, YearData>
   >((acc, item) => {
     const year = new Date(item.releaseDatetime).getFullYear().toString();
     const existing = acc[year];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84f1c83 and 4581874.

📒 Files selected for processing (3)
  • src/features/common/Layout/ProjectPropsContext.tsx (3 hunks)
  • src/features/projects/components/maps/Project.tsx (2 hunks)
  • src/utils/mapsV2/timeTravel.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/features/projects/components/maps/Project.tsx
  • src/features/common/Layout/ProjectPropsContext.tsx
🧰 Additional context used
📓 Learnings (1)
src/utils/mapsV2/timeTravel.ts (2)
Learnt from: mohitb35
PR: Plant-for-the-Planet-org/planet-webapp#2329
File: src/utils/mapsV2/timeTravel.ts:100-117
Timestamp: 2025-01-27T08:15:26.468Z
Learning: When validating GeoJSON Point coordinates at runtime, check if coordinates is an array and has exactly 2 elements (X,Y coordinates). The TypeScript Point type handles null checks, while the Position type allows for an optional third Z coordinate according to the GeoJSON spec.
Learnt from: mohitb35
PR: Plant-for-the-Planet-org/planet-webapp#2329
File: src/utils/mapsV2/timeTravel.ts:100-117
Timestamp: 2025-01-27T08:10:51.101Z
Learning: In geospatial applications, while TypeScript types (like `Point` from `geojson`) provide compile-time safety, runtime validation of coordinates is still necessary when dealing with external data sources to ensure data integrity and prevent runtime errors.
🔇 Additional comments (5)
src/utils/mapsV2/timeTravel.ts (5)

46-54: LGTM! Well-documented utility function.

The URL format conversion function is clear, focused, and properly documented.


85-93: LGTM! Well-structured type definitions.

The type definitions are clear, properly leveraging TypeScript features for type safety.


115-115: Verify and document the zoom level choice.

The zoom level is hardcoded to 13 with a TODO comment. Please verify if this is the optimal zoom level for the use case and document the reasoning.


102-126: LGTM! Robust implementation with proper validation.

The function implementation includes:

  • Proper coordinate validation (as discussed in previous reviews)
  • Error handling
  • Null safety checks

99-100: Update cache time configuration.

The cache time is temporarily set to 5 minutes instead of the intended 24 hours. Update this once testing is complete.

-  // TODO - change temp cache time
-  const CACHE_TIME_IN_SECONDS = 60 * 5; /* 60 * 60 * 24 */
+  const CACHE_TIME_IN_SECONDS = 60 * 60 * 24; // 24 hours

Copy link
Contributor

@shyambhongle shyambhongle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
src/utils/mapsV2/timeTravel.ts (6)

20-22: Consider moving the ESRI URL to environment variables.

The hardcoded ESRI map tiles URL should be moved to environment variables for better configuration management and security.

-const SOURCE_BASE_URLS: Record<SourceName, string> = {
-  esri: 'https://wayback.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/WMTS/1.0.0/default028mm/MapServer/tile',
-};
+const SOURCE_BASE_URLS: Record<SourceName, string> = {
+  esri: process.env.NEXT_PUBLIC_ESRI_WAYBACK_TILES_URL || '',
+};

24-25: Enhance the TODO comment with removal criteria.

The comment should specify when or under what conditions this code can be removed.

-// To remove once we remove the old maps code
+// TODO: Remove this function and associated JSON config when the legacy maps implementation is deprecated (tracked in issue #XXXX)

26-44: Add error handling for malformed configuration data.

The function should handle potential errors in the JSON configuration data structure.

 export const getTimeTravelConfig = (): TimeTravelConfig => {
   const result: TimeTravelConfig = {};
 
   for (const source of SOURCE_NAMES) {
+    try {
       const sourceData = timeTravelConfig[source]?.wayback;
       if (!sourceData) continue;
 
       result[source] = [];
 
       for (const [year, data] of Object.entries(sourceData)) {
         if (data?.id && data.id.length > 0) {
           const url = `${SOURCE_BASE_URLS[source]}/${data.id}/{z}/{y}/{x}`;
           result[source].push({ year, raster: url });
         }
       }
+    } catch (error) {
+      console.error(`Error processing time travel config for source ${source}:`, error);
+      continue;
+    }
   }
 
   return result;
 };

61-83: Enhance type safety for wayback items processing.

Consider adding runtime validation for the wayback items and their properties.

 const getLatestByYear = (items: WaybackItem[]): SingleYearTimeTravelData[] => {
+  if (!Array.isArray(items)) {
+    throw new Error('Expected array of wayback items');
+  }
+
   const intermediate = items.reduce<
     Record<string, { rasterUrl: string; timestamp: number }>
   >((acc, item) => {
+    if (!item.releaseDatetime || !item.itemURL) {
+      console.warn('Skipping invalid wayback item:', item);
+      return acc;
+    }
+
     const year = new Date(item.releaseDatetime).getFullYear().toString();
     const existing = acc[year];

114-114: Enhance the TODO comment about zoom level.

The comment should provide context about why zoom level 13 was chosen and what factors should be considered when updating it.

-      13 //TODO - confirm zoom level and update
+      13 // TODO: Validate if zoom level 13 provides optimal balance between detail and performance.
+         // Consider: tile size, coverage area, and ESRI service limits.

133-137: Enhance error logging with structured error information.

The error logging could be more informative by including project ID and structured error details.

   } catch (err) {
-    console.error('Error fetching time travel data:', err);
+    console.error('Error fetching time travel data:', {
+      projectId,
+      error: err instanceof Error ? err.message : String(err),
+      stack: err instanceof Error ? err.stack : undefined,
+    });
     // Return empty config on error to gracefully degrade
     return null;
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de2cab6 and a47a2f0.

📒 Files selected for processing (1)
  • src/utils/mapsV2/timeTravel.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/utils/mapsV2/timeTravel.ts (2)
Learnt from: mohitb35
PR: Plant-for-the-Planet-org/planet-webapp#2329
File: src/utils/mapsV2/timeTravel.ts:100-117
Timestamp: 2025-01-27T08:15:26.468Z
Learning: When validating GeoJSON Point coordinates at runtime, check if coordinates is an array and has exactly 2 elements (X,Y coordinates). The TypeScript Point type handles null checks, while the Position type allows for an optional third Z coordinate according to the GeoJSON spec.
Learnt from: mohitb35
PR: Plant-for-the-Planet-org/planet-webapp#2329
File: src/utils/mapsV2/timeTravel.ts:100-117
Timestamp: 2025-01-27T08:10:51.101Z
Learning: In geospatial applications, while TypeScript types (like `Point` from `geojson`) provide compile-time safety, runtime validation of coordinates is still necessary when dealing with external data sources to ensure data integrity and prevent runtime errors.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
src/utils/mapsV2/timeTravel.ts (2)

46-54: LGTM! Well-documented utility function.

The URL format conversion function is clear, focused, and properly documented.


85-93: LGTM! Well-structured type definitions.

The types provide good type safety and are properly exported for reuse.

@mariahosfeld mariahosfeld merged commit d166a52 into develop Jan 30, 2025
8 of 9 checks passed
@mariahosfeld mariahosfeld deleted the feature/project-time-travel branch January 30, 2025 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants